#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import json
import redis
from config.settings import settings
from utility.mongo_client import MongoGameState
from datetime import datetime
import logging

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class RedisCacheClient:
    def __init__(self,
                 redis_host=settings.CACHE_REDIS_HOST,
                 redis_port=settings.CACHE_REDIS_PORT,
                 redis_db=settings.CACHE_REDIS_DB,
                 redis_password=settings.CACHE_REDIS_PWD):
        """
        初始化Redis缓存客户端
        """
        try:
            self.redis_client = redis.Redis(
                host=redis_host,
                port=redis_port,
                db=redis_db,
                password=redis_password,
                decode_responses=True  # 自动解码响应
            )
            # 测试连接
            self.redis_client.ping()
            logging.info("Redis连接成功")
        except Exception as e:
            logging.error(f"Redis连接失败: {e}")
            self.redis_client = None

        # MongoDB客户端作为后备数据源
        self.mongo_client = MongoGameState()

    def save_user(self, user_id, user_data, expire_time=3600):
        """
        保存玩家数据到Redis缓存和MongoDB
        """
        if not self.redis_client:
            # 如果Redis不可用，直接使用MongoDB
            return self.mongo_client.save_user(user_id, user_data)

        try:
            # 将数据保存到Redis
            key = f"user:{user_id}"
            # 处理 created_at 字段
            created_at = user_data.get('profile', {}).get('created_at')
            if created_at is not None and isinstance(created_at, datetime):
                user_data['profile']['created_at'] = created_at.strftime("%Y-%m-%d %H:%M:%S")

            # 处理 last_login 字段
            last_login = user_data.get('profile', {}).get('last_login')
            if last_login is not None and isinstance(last_login, datetime):
                user_data['profile']['last_login'] = last_login.strftime("%Y-%m-%d %H:%M:%S")

            serialized_data = json.dumps(user_data)
            self.redis_client.setex(key, expire_time, serialized_data)

            # 同时保存到MongoDB（持久化存储）
            self.mongo_client.save_user(user_id, user_data)

            return True
        except Exception as e:
            logging.error(f"保存玩家数据到Redis失败: {e}")
            # Redis失败时仍然保存到MongoDB
            return self.mongo_client.save_user(user_id, user_data)

    def load_user(self, user_id):
        """
        从Redis缓存加载玩家数据，缓存未命中则从MongoDB加载并缓存
        """
        if not self.redis_client:
            # 如果Redis不可用，直接使用MongoDB
            return self.mongo_client.load_user(user_id)

        try:
            key = f"user:{user_id}"
            # 首先尝试从Redis获取
            cached_data = self.redis_client.get(key)

            if cached_data:
                # 缓存命中，直接返回
                return json.loads(cached_data)
            else:
                # 缓存未命中，从MongoDB加载
                user_data = self.mongo_client.load_user(user_id)
                if user_data:
                    # 将数据存入Redis缓存
                    serialized_data = json.dumps(user_data)
                    self.redis_client.setex(key, 3600, serialized_data)  # 默认1小时过期
                return user_data

        except Exception as e:
            logging.error(f"从Redis加载玩家数据失败: {e}")
            # Redis失败时直接从MongoDB加载
            return self.mongo_client.load_user(user_id)

    def find_user_by_openid(self, openid):
        """
        根据openid查找玩家数据，先从Redis缓存查找，缓存未命中则从MongoDB加载并缓存
        @param openid: 微信用户唯一标识
        @return: 玩家数据或None
        """
        if not self.redis_client:
            # 如果Redis不可用，直接从MongoDB获取
            return self.mongo_client.find_user_by_openid(openid)

        try:
            # 首先尝试从Redis获取
            key = f"user_openid:{openid}"
            cached_data = self.redis_client.get(key)

            if cached_data:
                # 缓存命中，直接返回
                return json.loads(cached_data)
            else:
                # 缓存未命中，从MongoDB加载
                user_data = self.mongo_client.find_user_by_openid(openid)
                if user_data:
                    # 将数据存入Redis缓存
                    serialized_data = json.dumps(user_data)
                    self.redis_client.setex(key, 3600, serialized_data)  # 默认1小时过期
                return user_data

        except Exception as e:
            logging.error(f"从Redis加载玩家数据失败: {e}")
            # Redis失败时直接从MongoDB加载
            return self.mongo_client.find_user_by_openid(openid)

    def save_token(self, token, token_data, expire_time=7200):
        """
        保存token信息到Redis缓存和MongoDB
        """
        if not self.redis_client:
            return self.mongo_client.save_token(token, token_data)

        try:
            # 保存到Redis
            key = f"token:{token}"
            serialized_data = json.dumps(token_data)
            self.redis_client.setex(key, expire_time, serialized_data)

            # 同时保存到MongoDB
            self.mongo_client.save_token(token, token_data)

            return True
        except Exception as e:
            logging.error(f"保存token到Redis失败: {e}")
            return self.mongo_client.save_token(token, token_data)

    def load_token(self, token):
        """
        从Redis缓存加载token信息，缓存未命中则从MongoDB加载并缓存
        """
        if not self.redis_client:
            return self.mongo_client.load_token(token)

        try:
            key = f"token:{token}"
            # 首先尝试从Redis获取
            cached_data = self.redis_client.get(key)

            if cached_data:
                # 缓存命中
                return json.loads(cached_data)
            else:
                # 缓存未命中，从MongoDB加载
                token_data = self.mongo_client.load_token(token)
                if token_data:
                    # 将数据存入Redis缓存
                    serialized_data = json.dumps(token_data)
                    # 使用token_data中的过期时间或默认2小时
                    expire_time = token_data.get('expires_in', 7200)
                    self.redis_client.setex(key, expire_time, serialized_data)
                return token_data

        except Exception as e:
            logging.error(f"从Redis加载token失败: {e}")
            return self.mongo_client.load_token(token)

    def delete_token(self, token):
        """
        从Redis和MongoDB中删除token
        """
        if not self.redis_client:
            return self.mongo_client.delete_token(token)

        try:
            # 从Redis删除
            key = f"token:{token}"
            self.redis_client.delete(key)

            # 从MongoDB删除
            return self.mongo_client.delete_token(token)
        except Exception as e:
            logging.error(f"从Redis删除token失败: {e}")
            return self.mongo_client.delete_token(token)

    def update_user_position(self, user_id, x, y, expire_time=3600):
        """
        更新玩家位置，同时更新Redis缓存和MongoDB
        """
        if not self.redis_client:
            return self.mongo_client.update_user_position(user_id, x, y)

        try:
            # 先获取现有玩家数据
            user_data = self.load_user(user_id)
            if not user_data:
                user_data = {"x": x, "y": y}
            else:
                user_data["x"] = x
                user_data["y"] = y

            # 更新Redis缓存
            key = f"user:{user_id}"
            serialized_data = json.dumps(user_data)
            self.redis_client.setex(key, expire_time, serialized_data)

            # 更新MongoDB
            return self.mongo_client.update_user_position(user_id, x, y)
        except Exception as e:
            logging.error(f"更新玩家位置到Redis失败: {e}")
            return self.mongo_client.update_user_position(user_id, x, y)

    def add_item_to_user(self, user_id, item, expire_time=3600):
        """
        为玩家添加物品，同时更新Redis缓存和MongoDB
        """
        if not self.redis_client:
            return self.mongo_client.add_item_to_user(user_id, item)

        try:
            # 获取玩家数据
            user_data = self.load_user(user_id)
            if not user_data:
                user_data = {"x": 0, "y": 0, "inventory": []}

            if "inventory" not in user_data:
                user_data["inventory"] = []

            user_data["inventory"].append(item)

            # 更新Redis缓存
            key = f"user:{user_id}"
            serialized_data = json.dumps(user_data)
            self.redis_client.setex(key, expire_time, serialized_data)

            # 更新MongoDB
            return self.mongo_client.save_user(user_id, user_data)
        except Exception as e:
            logging.error(f"添加物品到Redis失败: {e}")
            return self.mongo_client.add_item_to_user(user_id, item)

    def invalidate_user_cache(self, user_id):
        """
        使指定玩家的缓存失效
        """
        if not self.redis_client:
            return True

        try:
            key = f"user:{user_id}"
            self.redis_client.delete(key)
            return True
        except Exception as e:
            logging.error(f"使玩家缓存失效失败: {e}")
            return False

    def get_all_users(self):
        """
        获取所有玩家数据（直接从MongoDB获取，因为Redis中可能不完整）
        """
        return self.mongo_client.get_all_users()

    def close(self):
        """
        关闭数据库连接
        """
        if self.redis_client:
            self.redis_client.close()
        if self.mongo_client:
            self.mongo_client.close()
